package cn.com.dcsgo.config;

import cn.com.dcsgo.constant.AuthConstant;
import cn.com.dcsgo.constant.BusinessEnum;
import cn.com.dcsgo.constant.HttpConstant;
import cn.com.dcsgo.model.LoginResult;
import cn.com.dcsgo.model.Result;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSON;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.io.PrintWriter;
import java.time.Duration;

/**
 * 认证授权流程配置类
 *
 * @author Dcsgo
 * @since 2025/9/17 20:40
 */
@Slf4j
@Configuration
public class AuthSecurityConfig {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 配置 HTTP 安全规则，定义认证授权逻辑
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable();// 禁用 CSRF
        http.cors().disable();// 禁用跨域
        // 基于 token，所以不需要 session（不使用也不生成）
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 表单登录配置（自定义登录流程）
        http.formLogin()
                .usernameParameter("username")// 登录用户名参数名
                .passwordParameter("password")// 登录密码参数名
                .successHandler(successHandler())// 登录成功处理器
                .failureHandler(failureHandler())// 登录失败处理器
                .loginProcessingUrl(AuthConstant.LOGIN_URL) // 登录请求接口路径
                .permitAll();// 允许所有用户访问登录接口
        // 登出配置
        http.logout()
                .logoutUrl(AuthConstant.LOGOUT_URL)// 注销请求接口路径
                .logoutSuccessHandler(logoutSuccessHandler());// 注销成功处理器
        // 授权规则：除API文档外其他所有请求必须认证（登录后才能访问）
        http.authorizeHttpRequests(auth -> auth
                .requestMatchers(AuthConstant.RESOURCE_ALLOW_URLS).permitAll()
                .anyRequest().authenticated());
        return http.build();
    }

    /**
     * 登出成功处理器
     * 返回 JSON 格式数据，而非默认的页面跳转
     */
    @Bean
    public LogoutSuccessHandler logoutSuccessHandler() {
        return (request, response, authentication) -> {
            response.setContentType(HttpConstant.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstant.UTF_8);
            // 删除 Redis 中的认证信息
            String authorization = request.getHeader(AuthConstant.AUTHORIZATION);
            // 前端请求时token是放在请求头中，格式为 Authorization: Bearer xxx
            String token = authorization.replaceFirst(AuthConstant.BEARER, "");
            stringRedisTemplate.delete(AuthConstant.LOGIN_TOKEN_PREFIX + token);
            Result<Object> result = Result.success(null);
            String resultJson = JSON.toJSONString(result);
            PrintWriter writer = response.getWriter();
            writer.write(resultJson);
            writer.flush();
            writer.close();
        };
    }

    /**
     * 登录失败处理器
     * 返回 JSON 格式数据，而非默认的页面跳转
     */
    @Bean
    public AuthenticationFailureHandler failureHandler() {
        return (request, response, exception) -> {
            log.error("登录失败:{}", exception.getMessage());
            response.setContentType(HttpConstant.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstant.UTF_8);
            Result<String> result = new Result<>();
            result.setCode(BusinessEnum.UN_AUTHORIZATION.getCode());
            // 根据异常类型返回具体错误信息
            if (exception instanceof BadCredentialsException ||
                    exception instanceof InternalAuthenticationServiceException) {
                result.setMsg(exception.getMessage());
            } else if (exception instanceof AccountExpiredException) {
                result.setMsg("账号异常，请联系管理员");
            } else {
                result.setMsg(BusinessEnum.UN_AUTHORIZATION.getDesc());
            }
            String resultJson = JSON.toJSONString(result);
            PrintWriter writer = response.getWriter();
            writer.write(resultJson);
            writer.flush();
            writer.close();
        };
    }

    /**
     * 登录成功处理器
     * 返回 JSON 格式数据，而非默认的页面跳转
     */
    @Bean
    public AuthenticationSuccessHandler successHandler() {
        return (request, response, authentication) -> {
            response.setContentType(HttpConstant.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstant.UTF_8);
            // 将认证信息（token）存储到 Redis 中
            String token = IdUtil.fastSimpleUUID();
            String userStr = JSON.toJSONString(authentication.getPrincipal());
            stringRedisTemplate.opsForValue().set(
                    AuthConstant.LOGIN_TOKEN_PREFIX + token, userStr, Duration.ofSeconds(AuthConstant.TOKEN_TIME
                    ));
            LoginResult loginResult = new LoginResult(token, AuthConstant.TOKEN_TIME);
            Result<LoginResult> result = Result.success(loginResult);
            String resultJson = JSON.toJSONString(result);
            PrintWriter writer = response.getWriter();
            writer.write(resultJson);
            writer.flush();
            writer.close();
        };

    }

    /**
     * 密码加密
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
